#ifndef RESTAURANTE
#define RESTAURANTE

#include <iostream>
#include <string>
#include "DiccTrie.h"
#include "ConjString.h"
#include "Multiconjunto.h"
#include "Recetario.h"
#include <math.h>


using namespace std;

// estructura auxiliar
struct InfoMesa {
	string mozo;
	bool libre;
	MConj pedidos;

};

class Restaurante {

public:
	// el destructor de Restaurante
	~Restaurante();
	// el constructor de Restaurante
	Restaurante(int cantMesas, const ConjString &mozos, const DiccTrie<ConjString> &asignacion, const Recetario &recetario);
	bool ocuparMesa(int mesa);
	bool hacerPedido(int mesa, const MConj &pedido);
	void pagar(const int mesa);
	bool prepararPlato(const int mesa, const string &plato);
	void comprarIngrediente(const string &ingrediente, int cantidad);
	int cantMesas() const;
	const ConjString & verMozos() const;
	const Recetario & verRecetario() const;
	int dameStock(const string &ingrediente) const;
	const MConj &pedidosPendientes(int mesa) const;
	const string &mozoAsignado(int mesa) const;
	const ConjString &mesasDelMozo(const string &mozo) const;
	bool mesaOcupada(int mesa) const;
	int cantMesasMozo(const string &mozo) const;
	bool sePuedePreparar(const MConj &pedido) const;
	
	
	
private:
	
	/* si bien no estaba en el diseño se lo agregamos para obtener la cantidad de Mesas en orden 1 ya que el c++ no permite saber el tamaño de un arreglo
	   de tamaño brindado dinámicamente en O(1)
	*/
	int cantM;
	InfoMesa ** mesas; // Arreglo dimensionable de mesas con información sobre cada una de ellas encapsuladas en la estructura InfoMesa
	DiccTrie<ConjString> asignacion;
	MConj stock; // esto estaba mal en el tp diseño. En aquel habíamos usado  de otro modo había que setear el diccionario en 0 para todos los ingredientes del recetario
	Recetario recetario;	
	
	
};
// funciones auxiliares


/*************************************************************************************************************************
Aclaración importante: como contábamos solamente con un ConjString (un conjunto no paramétrico) y necesitábamos construir
la asignación de mesas (a cada mozo un conjunto de mesas), decidimos construir el diccionario con el que se implementa esa 
asignación pasándole strings que representan numeros (donde la a representa en el valor absoluto 0, la b el valor absoluto 1, 
etc.). De manera que el número 125 se representa como bcf.

***************************************************************************************************************************/
// esta función se hace necesaria para traducir a entero las mesas codificadas como caracteres en la asignación de los mozos.
int toInt(string str) { 
		int retorno = 0;
		int i = 0;
		int largo = str.length();
		while (i != largo) {
			retorno += (str[largo - i - 1] - 97) * (pow(10, i)); 
			i++;
			} 
		return retorno;
	}

// ver Aclaración importante
Restaurante::Restaurante(int cantMesas, const ConjString &mozos, const DiccTrie<ConjString> &asig, const Recetario &rec) {
	string moz;

	IteradorConjString it2;
	int mes;
	mesas = new InfoMesa*[cantMesas];
	int i = 0;

	// seteamos el arreglo de mesas
	while ( i < cantMesas ) {
		mesas[i] = new InfoMesa;
		mesas[i] -> libre = true;	
		i++;
	}
	IteradorConjString it(mozos);

	while (it.tieneProximo()) {
		moz = it.proximo();

		it2 = IteradorConjString(asig.obtener(moz));
		while( it2.tieneProximo()) {
			mes = toInt(it2.proximo());

			((mesas[mes]) -> mozo) = moz;
			((mesas[mes]) -> pedidos)= MConj();

	
		}
	}

	// seteamos los demás miembros de la estructura
	asignacion = asig;
	recetario = rec;
	cantM = cantMesas;

	

}
Restaurante::~Restaurante() {
	int cantM = cantMesas();
	int i = 0;
	while ( i < cantM ) {
		delete mesas[i];
	
		i++;
	}
	delete [] mesas;


}
bool Restaurante::ocuparMesa(int mesa) {
	
	if ( !(this -> mesaOcupada(mesa)) && mesa < this->cantMesas() ) {
		(mesas)[mesa] -> libre = false;
		return true;
	}
	else {
	 	return false;
	}
}

bool Restaurante::hacerPedido(int mesa, const MConj &pedido) {
	
	bool ret = false;
	int stockRequerido;


	string ingTemp;

	if ( (this -> mesaOcupada(mesa)) && mesa < this->cantMesas() ) {

			
		if ( pedido.mTieneTodos(recetario.platos()) ) {

			if ( this->sePuedePreparar(pedido) ) {
				( (mesas)[mesa] -> pedidos ).mUnion(pedido);
				ret = true;

				ConjString ingredientes(recetario.ingredientesNecesariosPedido(pedido));
				IteradorConjString iterIng(ingredientes);
				cout << iterIng.proximo();
				while( iterIng.tieneProximo() ) {
					ingTemp = iterIng.proximo();
					stockRequerido = recetario.cantidadNecesariaPedido(pedido, ingTemp);
					stock.mQuitarN(ingTemp, stockRequerido);
				}

			}
		}
	
	}
	return ret;

}

void Restaurante::pagar(const int mesa) {
// en la precondición está que tiene que no tener platos pendientes
	(mesas)[mesa] -> libre = true;
}

bool Restaurante::prepararPlato(const int mesa, const string &plato) {
	
	bool ret = false;
	
	if ( 	mesa < this->cantMesas() && (this -> mesaOcupada(mesa)) && 
		(this->pedidosPendientes(mesa)).mPertenece(plato) ) {
		
		((mesas)[mesa] -> pedidos).mQuitarUno(plato);
		ret = true;
	}
	
	return ret;

}

void Restaurante::comprarIngrediente(const string &ingrediente, int cantidad) {
	
	stock.mAgregarN(ingrediente, cantidad);
	
}	

// Esto no cambia el orden de las funciones relevantes. Otra opción era construir una clase ArregloDinámico que nos devolviera en orden 1 el tamaño.
int Restaurante::cantMesas() const {	// duda, capaz hace cualquiera
	

	return cantM;

}

const ConjString & Restaurante::verMozos() const {
	return asignacion.claves();
}	

const Recetario & Restaurante::verRecetario() const {
	return recetario;
}

int Restaurante::dameStock(const string &ingrediente) const {

	return  stock.mCantidad(ingrediente);;
}

const MConj & Restaurante::pedidosPendientes(int mesa) const {
	return (mesas)[mesa] -> pedidos;
}

const string & Restaurante::mozoAsignado(int mesa) const {
	return (mesas)[mesa] -> mozo;
}

const ConjString & Restaurante::mesasDelMozo(const string &mozo) const {
	return asignacion.obtener(mozo);
}

bool Restaurante::mesaOcupada(int mesa) const {
	return !((mesas)[mesa] -> libre);
}

int Restaurante::cantMesasMozo(const string &mozo) const {
	return (asignacion.obtener(mozo)).dameTamano();
}


bool Restaurante::sePuedePreparar(const MConj &pedido) const {

	bool ret = true;
	MConj ingsPedido = recetario.ingredientesPedido(pedido);
	IteradorMConj it(ingsPedido);
	string ingrediente;

	while ( it.tieneProximo() ) {
		ingrediente = it.proximo();
		if ( stock.mCantidad(ingrediente) <= ingsPedido.mCantidad(ingrediente) )
			ret = false;
	}
	
	return ret;

}

#endif
